﻿using System;
using System.Net;
using ElmahCore;
using ElmahCore.Mvc;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using net.xBei.Helper;
using net.xBei.Models;
using net.xBei.WebApi.Helper;

namespace net.xBei.WebApi.FrameworkExtensions {
    /// <summary>
    /// <see href="https://github.com/ElmahCore/ElmahCore">Elmah</see>
    /// </summary>
    public static class ElmahExtensions {
        /// <summary>
        /// 是否设置Elmah
        /// </summary>
        private static bool SetupElmah;
        /// <summary>
        /// 配置
        /// </summary>
        /// <param name="app"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseCustomElmah(this IApplicationBuilder app) {
            if (SetupElmah) {
                app.UseElmah();
            }
            return app;
        }
        /// <summary>
        /// 配置<see href="https://github.com/ElmahCore/ElmahCore">Elmah</see>，默认只有非生产环境或者已经登录的用户才能访问
        /// </summary>
        /// <param name="services"></param>
        /// <param name="env"></param>
        /// <param name="setupOptions"></param>
        public static IServiceCollection ConfigElmah(this IServiceCollection services,
                                                     IWebHostEnvironment env,
                                                     Action<ElmahOptions>? setupOptions = default)
            => ConfigElmah(services, 
                           context => !env.IsProduction() 
                                                    || (context.User.Identity?.IsAuthenticated == true
                                                        && context.User.GetIntValue(AuthorizationHandler.ClaimUserType, 0)
                                                                       .GetEnum(CommonConsts.UserType.GUEST)
                                                                       .HasFlag(CommonConsts.UserType.BACKEND) == true
                                                    ), 
                          setupOptions);
        /// <summary>
        /// 配置<see href="https://github.com/ElmahCore/ElmahCore">Elmah</see>
        /// </summary>
        /// <param name="services"></param>
        /// <param name="OnPermissionCheck"></param>
        /// <param name="setupOptions"></param>
        public static IServiceCollection ConfigElmah(this IServiceCollection services,
                                                     Func<HttpContext, bool> OnPermissionCheck,
                                                     Action<ElmahOptions>? setupOptions = default) {
            services.AddElmah(options => {
                options.EventLog = new ElmahCore.MemoryErrorLog(100);
                options.OnPermissionCheck = OnPermissionCheck;
                options.Path = "el/mah";
                //options.Notifiers.Add(new CommonLoggerNotifier("UnhandledException",
                //    services.BuildServiceProvider().GetService<ILogger<CommonLoggerNotifier>>()
                //    ));
                //options.Notifiers.Add(new CommonLoggerNotifier("UnhandledException", services.BuildServiceProvider().GetService<AliLogger>()));
                //options.FiltersConfig = "elmah.xml";
                setupOptions?.Invoke(options);
            });
            SetupElmah = true;
            return services;
        }

        class CommonLoggerNotifier : IErrorNotifier {
            public string Name { get; }
            private readonly ILogger? commonLogger;

            public void Notify(Error error) {
                if (error.StatusCode == (int)HttpStatusCode.NotFound) {
                    return;
                }
                commonLogger?.LogError(new EventId(0, Name),
                                       exception: error.Exception,
                                       message: "message:{Message}, {HostName},{Source}",
                                       error.Message,
                                       error.HostName,
                                       error.Source);
            }

            public CommonLoggerNotifier(string name, ILogger? commonLogger) {
                Name = name;
                this.commonLogger = commonLogger;
            }
        }
    }
}
